#include "dht11.h"
#include "includes.h"
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include "delay.h"
#include "sys.h"
#include "usart.h"


RTC_DateTypeDef  RTC_DateStructure;
RTC_TimeTypeDef  RTC_TimeStructure;


/**
 @brief 温湿度模块初始化
*/
void dht11_init(void)
{
	GPIO_InitTypeDef  GPIO_InitStructure;
	RCC_AHB1PeriphClockCmd(RCC_AHB1Periph_GPIOG,ENABLE);

	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;	//指定9号引脚
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;//配置为输出模式
	GPIO_InitStructure.GPIO_OType = GPIO_OType_OD;//开漏输出
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//速度越高，功耗就越高，但是响应速度也更快
	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;//不需要使能内部上下拉电阻
	GPIO_Init(GPIOG, &GPIO_InitStructure);

	//看时序图，初始电平状态为高电平
	PGout(9)=1;
}

/**
 @brief 读取温湿度信息
 @param buf: buf[0]:湿度整数部分
			 buf[1]:湿度小数部分
			 buf[2]:温度整数部分
			 buf[3]:温度小数部分
			 buf[4]:校验和,buf[0]+buf[1]+buf[2]+buf[3]
 @retval 获取正确为0,错误非0
*/
int32_t dht11_read(uint8_t *buf)
{
	GPIO_InitTypeDef  GPIO_InitStructure;
	uint8_t *p = buf;
	uint32_t t=0;
	int32_t i,j;
	uint8_t d=0;
	uint8_t check_sum=0;
	
	//等待互斥锁
	xSemaphoreTake(xMutex, portMAX_DELAY);

	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;	//指定9号引脚
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_OUT;//配置为输出模式
	GPIO_InitStructure.GPIO_OType = GPIO_OType_OD;//开漏输出
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//速度越高，功耗就越高，但是响应速度也更快
	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;//不需要使能内部上下拉电阻
	GPIO_Init(GPIOG, &GPIO_InitStructure);

	PGout(9)=0;
	delay_ms(20);
	PGout(9)=1;	
	delay_us(30);

	GPIO_InitStructure.GPIO_Pin = GPIO_Pin_9;	//指定9号引脚
	GPIO_InitStructure.GPIO_Mode = GPIO_Mode_IN;//配置为输入模式
	GPIO_InitStructure.GPIO_Speed = GPIO_Speed_100MHz;//速度越高，功耗就越高，但是响应速度也更快
	GPIO_InitStructure.GPIO_PuPd = GPIO_PuPd_NOPULL;//不需要使能内部上下拉电阻
	GPIO_Init(GPIOG, &GPIO_InitStructure);	

	
	//等待低电平出现
	t=0;
	while(PGin(9))
	{
		delay_us(1);
		t++;
		
		if(t >=4000){
			xSemaphoreGive(xMutex);
			return -1;
		}
			
	}
	
	//检测低电平的有效性
	t=0;
	while(PGin(9)==0)
	{
		delay_us(1);
		t++;
		
		if(t >=100){
			xSemaphoreGive(xMutex);
			return -2;
		}
			
	}	
	
	
	//检测高电平的有效性
	t=0;
	while(PGin(9))
	{
		delay_us(1);
		t++;
		
		if(t >=100){
			xSemaphoreGive(xMutex);
			return -3;
		}
	}

	for(j=0; j<5; j++)
	{
		d=0;
		
		for(i=7; i>=0; i--)
		{
			//成功判断一个数据0或数据1
			
			//等待前置的50us电平持续完毕
			t=0;
			while(PGin(9)==0)
			{
				delay_us(1);
				t++;
				
				if(t >=100){
					xSemaphoreGive(xMutex);
					return -4;
				}
			}

			//延时一会，延时多长时间才合适呢(30us~70us)
			delay_us(40);
			
			if(PGin(9))
			{
				d|=1<<i;
				
				//等待剩下的高电平持续完毕
				t=0;
				while(PGin(9))
				{
					delay_us(1);
					t++;
					
					if(t >=100){
						xSemaphoreGive(xMutex);
						return -5;
					}
				}				
			}
		
		}
		
		p[j]=d;
		
	}
	
	//通过校验和检测数据的有效性
	check_sum = (p[0]+p[1]+p[2]+p[3])&0xFF;
	
	if(check_sum !=p[4]){
		xSemaphoreGive(xMutex);
		return -6;
	}
	
	xSemaphoreGive(xMutex);
	return 0;
}


/**
 @brief 温湿度记录选项
 @param recv_buf: 命令选项
 @retval 获取正确为0,错误非0
*/
uint8_t ht_record_option(volatile int8_t *recv_buf, uint32_t *sector_4_addr, uint8_t dht11_record_size){
	uint8_t dht11_record_sw = 0;  // 温湿度记录开关
	uint8_t dht11_record_pos = 0;  // 温湿度记录的位置
	uint32_t max_addr_offset = 0;  // 地址最大偏移
	uint32_t addr_offset = 0;  // 地址偏移
	
	// 开始记录温湿度
	if(strstr((const char *)recv_buf,"start"))
	{
		dht11_record_sw = 1;
	}
	// 停止记录温湿度
	else if(strstr((const char *)recv_buf,"stop"))
	{
		dht11_record_sw = 0;
	}
	// 显示温湿度记录
	else if(strstr((const char *)recv_buf,"show"))
	{
		max_addr_offset = 0x08010000+dht11_record_size*11;  // 防止dht11_record_size变动对显示的干扰
		addr_offset = 0x08010000;
		while(addr_offset < max_addr_offset){
			printf("[%03d]20%02x/%02x/%02x Week %x %02x:%02x:%02x Temp=%d.%d Humi=%d.%d\r\n",
			++dht11_record_pos,
			*(__IO uint8_t*)(addr_offset),
			*(__IO uint8_t*)(addr_offset+0x1),
			*(__IO uint8_t*)(addr_offset+0x2),
			*(__IO uint8_t*)(addr_offset+0x3),
			*(__IO uint8_t*)(addr_offset+0x4),
			*(__IO uint8_t*)(addr_offset+0x5),
			*(__IO uint8_t*)(addr_offset+0x6),
			*(__IO uint8_t*)(addr_offset+0x7),
			*(__IO uint8_t*)(addr_offset+0x8),
			*(__IO uint8_t*)(addr_offset+0x9),
			*(__IO uint8_t*)(addr_offset+0xa)
			);
			addr_offset = addr_offset + 0xb;
		}
	}
	// 清空温湿度记录
	else if(strstr((const char *)recv_buf,"clear"))
	{
		dht11_record_sw = 0;
		*sector_4_addr = 0x08010000;
		
		//flash解锁，才能进行扇区擦除与写入数据
		FLASH_Unlock();
	
		//按32位数据来擦除整个扇区
		FLASH_EraseSector(FLASH_Sector_4,VoltageRange_3);
	
		//flash锁定，不能再对flash进行修改
		FLASH_Lock();
	}
	
	dht11_record_pos = 0;
	addr_offset = 0;
	max_addr_offset = 0;
	
	return dht11_record_sw;
}


/**
 @brief 温湿度记录
 @param recv_buf: 命令选项
 @retval 温湿度记录数量
*/
uint8_t ht_record(uint32_t *sector_4_addr, uint8_t dht11_record_size)
{
	uint8_t  buf[5];

	if(dht11_record_size > 100){
		printf("存储已满\r\n");
		return dht11_record_size;
	}
	
	RTC_GetDate(RTC_Format_BCD, &RTC_DateStructure);
	RTC_GetTime(RTC_Format_BCD, &RTC_TimeStructure);
	
	printf("[%03d]20%02x/%02x/%02x Week %x %02x:%02x:%02x Temp=%d.%d Humi=%d.%d\r\n",
	dht11_record_size+1,
	RTC_DateStructure.RTC_Year,RTC_DateStructure.RTC_Month,RTC_DateStructure.RTC_Date,RTC_DateStructure.RTC_WeekDay,
	RTC_TimeStructure.RTC_Hours,RTC_TimeStructure.RTC_Minutes,RTC_TimeStructure.RTC_Seconds,
	buf[2],buf[3],buf[0],buf[1]);
	
	//flash解锁，才能进行扇区擦除与写入数据
	FLASH_Unlock();
	
	if(dht11_record_size <= 100){
		FLASH_ProgramByte((*sector_4_addr)++, RTC_DateStructure.RTC_Year);     // 年
		FLASH_ProgramByte((*sector_4_addr)++, RTC_DateStructure.RTC_Month);    // 月
		FLASH_ProgramByte((*sector_4_addr)++, RTC_DateStructure.RTC_Date);     // 日
		FLASH_ProgramByte((*sector_4_addr)++, RTC_DateStructure.RTC_WeekDay);  // 星期
		FLASH_ProgramByte((*sector_4_addr)++, RTC_TimeStructure.RTC_Hours);    // 时
		FLASH_ProgramByte((*sector_4_addr)++, RTC_TimeStructure.RTC_Minutes);  // 分
		FLASH_ProgramByte((*sector_4_addr)++, RTC_TimeStructure.RTC_Seconds);  // 秒
		FLASH_ProgramByte((*sector_4_addr)++, buf[2]);  						// 温度整数
		FLASH_ProgramByte((*sector_4_addr)++, buf[3]);  						// 温度小数
		FLASH_ProgramByte((*sector_4_addr)++, buf[0]); 						// 湿度整数
		FLASH_ProgramByte((*sector_4_addr)++, buf[1]);							// 湿度小数
		dht11_record_size++;
	}
	
	//flash锁定，不能再对flash进行修改
	FLASH_Lock();
	
	return dht11_record_size;
}
